package com.staples.tc.cCardProcessor;

import static com.staples.tc.util.TCIConstants.DEF_ENV;
import static com.staples.tc.util.TCIConstants.NODEPATH_COMP_NAME;
import static com.staples.tc.util.TCIConstants.NODEPATH_PM_NMB;
import static com.staples.tc.util.TCIConstants.NODEPATH_PM_TYPE;
import static com.staples.tc.util.TCIConstants.NODEPATH_REWARDS_NMB;
import static com.staples.tc.util.TCIConstants.PM_TYPE_CCARD;
import static com.staples.tc.util.TCIConstants.SYS_PROP_ENV;
import static com.staples.tc.util.TCIConstants.*;

import java.io.StringReader;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.staples.tc.dao.CCardDAO;
import com.staples.tc.exception.CCardJobException;
import com.staples.tc.queueController.CCardQConnManager;
import com.staples.tc.queueController.RevRecQConnManager;
import com.staples.tc.util.CCardJobUtil;

/**
 * Enterprise batch job to consume message from C-Card Queue
 * @author Cognizant Technology Solutions.
 *
 */
public class CCardLookupJob {
	private static final Logger logger = Logger.getLogger(CCardLookupJob.class);

	public static void main(String[] args) {
		logger.info("+++++++++++++++++++ JOB STARTED +++++++++++++++++++");
		
		if (args != null && args.length > 0 && args[0] != null) {
			System.setProperty(SYS_PROP_ENV, args[0]);
		} else {
			System.setProperty(SYS_PROP_ENV, DEF_ENV);
		}
		
		long startTime = System.currentTimeMillis();
		
		CCardLookupJob cCardJob = new CCardLookupJob();
		CCardJobUtil utility = new CCardJobUtil();
		CCardDAO dao = new CCardDAO();
		CCardQConnManager ccardConnMgr = new CCardQConnManager();
		Document document = null;
		String[] msgArr = null;
		try {
			// Read all messages from CCard queue
			List<String> cCardMessageList = ccardConnMgr.readAllCCardQMessages();
			int cCardMessageListSize = cCardMessageList.size();

			if (cCardMessageListSize > 0) {
				msgArr = cCardJob.loadMessages(cCardMessageList);
				
				// Get DB Connection
				boolean checkDBConnection = false;
				checkDBConnection = dao.createDbConnection();
				if (checkDBConnection) {
					logger.debug("DB connection success ....");
				} else {
					logger.debug("DB connection could not be established ....");
				}
				
				for (int i = 0; i < cCardMessageListSize; i++) {
					String cCardMessage = cCardMessageList.get(i);
					
					// Parse queue message
					try {
						DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
						DocumentBuilder builder = factory.newDocumentBuilder();
						document = builder.parse(new InputSource(new StringReader(cCardMessage)));
					} catch (Exception e) {
						logger.error("Exception occured while parsing ...", e);
						ccardConnMgr.writeCCardQMessage(cCardMessage);
						logger.info("XML FORMAT ERRORED MESSAGE SENT SUCCESSFULLY BACK TO CCARD QUEUE >>>>>>>>");
						
						utility.sendBadInputAlertMail();
						logger.info("XML FORMAT ERROR MAIL SENT SUCCESSFULLY >>>>>");
						cCardJob.updateProcessedMsg(msgArr, i);
						continue;
					}

					if (document != null) {
						// Fetch CCard or Default Card from DB
						String cCard_number = cCardJob.getDbCcard(dao, document);
						logger.debug("CCard value from database :" + cCard_number);
						
						if (CCardJobUtil.isStrNotEmpty(cCard_number)) {
							// updateCanonical with CCard number
							logger.debug("Updating canonical with CCard number ....");
							Document updtdCanonical = cCardJob.updateCanonical(document, cCard_number);

							String strUpdtdCanonical = utility.documentToStringConv(updtdCanonical);
							logger.debug("Updated canonical with CCard number :" + CCardJobUtil.stripWhiteSpaces(strUpdtdCanonical));
							
							if (CCardJobUtil.isStrNotEmpty(strUpdtdCanonical)) {
								RevRecQConnManager revrevQMgr = new RevRecQConnManager();
								revrevQMgr.writeMessage(strUpdtdCanonical);
								logger.info("MESSAGE SENT SUCCESSFULLY TO REVREC QUEUE >>>>>>>>");
							}
						} else {
							// Send message back to queue
							ccardConnMgr.writeCCardQMessage(cCardMessage);
							logger.info("ERRORED MESSAGE SENT SUCCESSFULLY BACK TO CCARD QUEUE >>>>>>>>");
							
							// Send mail alert
							logger.debug("Sending Mail ....");
							String orderNum = cCardJob.getNodeVal(document, NODEPATH_ORD_NMB);
							String rewardNum = cCardJob.getNodeVal(document, NODEPATH_REWARDS_NMB);
							String companyName = cCardJob.getNodeVal(document, NODEPATH_COMP_NAME);
							utility.sendAlertMail(orderNum, rewardNum, companyName);
							logger.info("ERROR MAIL SENT SUCCESSFULLY >>>>>");
						}
					} else {
						logger.info("DOCUMENT OBJECT IS NULL >>>> :" + document);
						// Send message back to queue
						ccardConnMgr.writeCCardQMessage(cCardMessage);
						logger.info("ERRORED MESSAGE SENT SUCCESSFULLY BACK TO CCARD QUEUE >>>>>>>>");
						
						// Send mail alert
						utility.sendBadInputAlertMail();
						logger.info("ERROR MAIL SENT SUCCESSFULLY >>>>>");
					}
					
					// Remove processed message from array 
					cCardJob.updateProcessedMsg(msgArr, i);
				}
			} else {
				logger.info("NO MESSAGES IN THE REQUEST (CCARD) QUEUE >>>>>>>>>>>>>");
			}

		} catch (CCardJobException ce) {
			logger.error("CCardJobException occured in Main ....", ce);
			ce.printStackTrace();
		} catch (Exception exp) {
			logger.error("Exception occured in Main ....", exp);
			exp.printStackTrace();
		} finally {
			try {
				cCardJob.writeUnprocessedCCardQMsgs(msgArr);
			} catch (CCardJobException cExp) {
				logger.error("CCardJobException occured in Main while writing unprocessed messages ....", cExp);
				cCardJob.logUnprocessedCCardQMsgs(msgArr);
			}
			document = null;
			dao.closeAllDbResources();
		}
		logger.info("Time Taken in MS to complete the Job :" + (System.currentTimeMillis() - startTime));
		logger.info("+++++++++++++++++++ JOB ENDED +++++++++++++++++++");
	}
	
	/**
	 * 
	 * @param msgList
	 * @return
	 */
	private String[] loadMessages (List<String> msgList) {
		String[] msgArr = null;
		if (msgList.size() > 0) {
			msgArr = new String[msgList.size()];
			for (int i = 0; i < msgList.size(); i++) {
				msgArr[i] = msgList.get(i);
			}
		}
		return msgArr;
	}
	
	
	/**
	 * 
	 * @param msgArr
	 * @param index
	 */
	private void updateProcessedMsg (String[] msgArr, int index) {
		msgArr[index] = null;
	}
	
	
	/**
	 * 
	 * @param msgArr
	 * @throws CCardJobException 
	 */
	private void writeUnprocessedCCardQMsgs (String[] msgArr) throws CCardJobException {
		if (msgArr != null && msgArr.length > 0) {
			CCardQConnManager ccardConnMgr = new CCardQConnManager();
			for (int i = 0; i < msgArr.length; i++) {
				if (CCardJobUtil.isStrNotEmpty(msgArr[i])) {
					ccardConnMgr.writeCCardQMessage(msgArr[i]);
					logger.info("UNPROCESSED MESSAGE SENT BACK SUCCESSFULLY TO CCARD QUEUE >>>>>>>");
					this.updateProcessedMsg(msgArr, i);
				}
			}
		}
	}
	
	/**
	 * 
	 * @param msgArr
	 * @throws CCardJobException 
	 */
	private void logUnprocessedCCardQMsgs (String[] msgArr) {
		if (msgArr != null && msgArr.length > 0) {
			for (int i = 0; i < msgArr.length; i++) {
				if (CCardJobUtil.isStrNotEmpty(msgArr[i])) {
					logger.error("UNPROCESSED MESSAGE >>>>>>>>>>> " + i + ">>>>>>>>> :" + msgArr[i]);
				}
			}
		}
	}
	

	/**
	 * Update Canonical with CCard number/Default CCard Number from DB
	 * @param document
	 * @param card_number 
	 * @return
	 */
	private Document updateCanonical(Document document, String card_number) throws CCardJobException {
		logger.debug("Entering updateCanonical() ....");
		try {
			StringBuilder sb = new StringBuilder();

			sb.append(card_number);
			String strValue = sb.toString();

			javax.xml.xpath.XPath xpath = XPathFactory.newInstance().newXPath();

			NodeList updateCardNodeList = (NodeList) xpath.evaluate(NODEPATH_PM_NMB, document, XPathConstants.NODESET);
			logger.debug("NodeList length :" + updateCardNodeList.getLength());
			for (int i = 0; i < updateCardNodeList.getLength(); i++) {
				Node cardNode = updateCardNodeList.item(i);
				cardNode.setTextContent(strValue);
				logger.debug("Node value after change :" + cardNode.getTextContent());
			}

			NodeList updateTypeNodeList = (NodeList) xpath.evaluate(NODEPATH_PM_TYPE, document, XPathConstants.NODESET);
			logger.debug("Type node length :" + updateTypeNodeList.getLength());
			for (int j = 0; j < updateTypeNodeList.getLength(); j++) {
				Node typeNode = updateTypeNodeList.item(j);
				typeNode.setTextContent(PM_TYPE_CCARD);
			}

		} catch (Exception e) {
			logger.error("Exception occured while updating Canonical ..." + e.getMessage());
			// e.printStackTrace();
			throw new CCardJobException(e.getMessage(), e);
		}

		logger.debug("Exiting updateCanonical() ....");
		return document;
	}

	
	/**
	 * Method to fetch CCard number from DB. 
	 * If a CCard# will be returned if match found for Rewards# or 
	 * Default CCard# will be returned if match found for Company name or 
	 * NULL will be returned in case of no match
	 * @param rewardNumber
	 * @return
	 * @throws Exception
	 */
	private String getDbCcard(CCardDAO dao, Document document) throws CCardJobException {
		logger.debug("Entering getDbCcard() ....");
		String rewardNumber = null;
		String companyName = null;
		String cCardNumber = null;
		try {
			if (document != null) {
				rewardNumber = this.getNodeVal(document, NODEPATH_REWARDS_NMB);
				logger.debug("Reward number :" + rewardNumber);
				cCardNumber = dao.getCCard(rewardNumber);
				logger.debug("Rewards CCard :" + cCardNumber);
				
				if (cCardNumber == null || cCardNumber.trim().equals("")) {
					companyName = this.getNodeVal(document, NODEPATH_COMP_NAME);
					logger.debug("Company name :" + companyName);

					// Get Default card from database
					cCardNumber = dao.getDefaultCard(companyName);
					logger.debug("Company Default CCard :" + cCardNumber);
				}
			}
		} catch (CCardJobException ce) {
			logger.error("CCardJobException exception occured while fetching CCard from DB ...", ce);
			throw ce;
		} catch (Exception e) {
			logger.error("Exception exception occured while fetching CCard from DB ..." + e.getMessage());
//			e.printStackTrace();
			throw new CCardJobException(e.getMessage(), e);
		}

		logger.debug("Exiting getDbCcard() ....");
		return cCardNumber;
	}

	/**
	 * Get Reward Number from message
	 * @param document
	 * @return
	 */
	public String getNodeVal(Document document, String nodePath) throws CCardJobException {
		logger.debug("Entering getNodeVal() ....");
		String rNumber = null;
		// int number=0;

		try {
			javax.xml.xpath.XPath xpath = XPathFactory.newInstance().newXPath();
			NodeList nodeList = (NodeList) xpath.evaluate(nodePath, document, XPathConstants.NODESET);

			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				rNumber = node.getTextContent();
				// logger.debug("Reward number"+rNumber);
			}
		} catch (Exception e) {
			logger.error("Exception occured while getting Node value ..." + e.getMessage() + ": Node :" + nodePath);
			// e.printStackTrace();
			throw new CCardJobException(e.getMessage(), e);
		}

		logger.debug("Exiting getNodeVal() ....");
		return rNumber;
	}
}
